package com.dss.auto_deploy.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dss.auto_deploy.config.DssProperties;
import com.dss.auto_deploy.constants.AutoConstants;
import com.dss.auto_deploy.domain.AdmDocker;
import com.dss.auto_deploy.domain.AdmDockerRegistry;
import com.dss.auto_deploy.domain.Server;
import com.dss.auto_deploy.dto.*;
import com.dss.auto_deploy.enums.BooleanTypeEnum;
import com.dss.auto_deploy.mapper.AdmDockerMapper;
import com.dss.auto_deploy.mapper.AdmDockerRegistryMapper;
import com.dss.auto_deploy.mapper.ServerMapper;
import com.dss.auto_deploy.service.IServerService;
import com.dss.auto_deploy.util.*;
import com.dss.auto_deploy.vo.FolderVo;
import com.dss.auto_deploy.vo.ServerVo;
import com.dss.auto_deploy.vo.SftpDownloadVo;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.Session;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.CharEncoding;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author dongshoushan
 * @Description:虚拟机服务器Service实现类
 * @date 2022-09-06 15:29:28
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class ServerServiceImpl extends ServiceImpl<ServerMapper, Server> implements IServerService, ApplicationContextAware {

    /**
     * 虚拟机服务器Mapper
     */
    private final ServerMapper serverMapper;
    private final AdmDockerRegistryMapper admDockerRegistryMapper;
    private final AdmDockerMapper admDockerMapper;
    private final CacheUtils cacheUtils;
    private ApplicationContext applicationContext;
    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private final DssProperties dssProperties;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 新增虚拟机服务器
     *
     * @param serverAddDto
     * @return Long
     */
    @Override
    public int insert(ServerAddDto serverAddDto) {
        Server server = BeanUtils.copy(serverAddDto, Server.class);
        server.setCreateTime(new Date());
        server.setUpdateTime(new Date());
        int insert = serverMapper.insert(server);
        if (insert == 1) {
            log.info("新增成功{}", JacksonUtils.toString(server));
        }
        cacheUtils.deleteObject("getAllServer");
        return insert;
    }


    /**
     * 修改虚拟机服务器
     *
     * @param serverUpdateDto
     * @return Long
     */
    @Override
    public int update(ServerUpdateDto serverUpdateDto) {
        Server server = BeanUtils.copy(serverUpdateDto, Server.class);
        int number = serverMapper.updateById(server);
        if (number == 1) {
            log.info("修改成功{}", JacksonUtils.toString(server));
        }
        cacheUtils.deleteObject(server.getId().toString());
        this.getServerById(server.getId());
        cacheUtils.deleteObject("getAllServer");
        return number;
    }


    /**
     * 批量删除虚拟机服务器
     *
     * @param ids
     * @return Long
     */
    @Override
    public void deleteBatch(Long[] ids) {
        List<Long> longs = Arrays.asList(ids);
        this.removeByIds(longs);
        for (Long id : longs) {
            cacheUtils.deleteObject(id.toString());
        }
        cacheUtils.deleteObject("getAllServer");
        log.info("删除成功{}", (Object) ids);
    }

    /**
     * 查询所有文件夹
     *
     * @return
     */
    @Override
    public List<FolderVo> getAllFolder() {
        QueryWrapper<Server> wrapper = new QueryWrapper<>();
        wrapper.eq("type", 0).orderByAsc("sort");
        List<Server> servers = serverMapper.selectList(wrapper);
        List<FolderVo> list = new ArrayList<>();
        servers.forEach(server -> {
            FolderVo build = FolderVo.builder().id(server.getId()).spread(false).title(server.getName())
                    .parentId(server.getParentId()).build();
            list.add(build);
        });
        FolderVo root = FolderVo.builder().id(-1L).spread(true).title("根节点").parentId(-2L).build();
        list.add(root);
        FolderVo f = getChildren(-1L, list);
        List<FolderVo> result = new ArrayList<>();
        result.add(f);
        return result;
    }

    /**
     * 查询所有虚拟机
     */
    @Override
    public List<ServerVo> getAllServer(String ip) {
        QueryWrapper<Server> wrapper = new QueryWrapper<>();
        wrapper.eq("type", 1).orderByAsc("sort");
        if (StringUtils.isNotEmpty(ip)) {
            wrapper.like("ip", ip);
        }
        List<Server> servers = serverMapper.selectList(wrapper);
        return BeanUtils.copyList(servers, ServerVo.class);
    }

    /**
     * 根据id查询服务器配置
     *
     * @param id
     * @return
     */
    @Override
    public Server getServerById(Long id) {
        Server server = cacheUtils.getCacheObject(id.toString());
        if (server == null) {
            server = serverMapper.selectById(id);
            cacheUtils.setCacheObject(id.toString(), server);
        }
        return server;
    }

    @Override
    public Server getServerByIp(String ip) {
        Server server = cacheUtils.getCacheObject(ip);
        if (server == null) {
            QueryWrapper<Server> wrapper = new QueryWrapper<>();
            wrapper.eq("ip", ip);
            server = serverMapper.selectOne(wrapper);
            cacheUtils.setCacheObject(ip, server);
        }
        return server;
    }

    @Override
    public Server getServerByRegistryId(Long registryId) {
        AdmDockerRegistry admDockerRegistry = admDockerRegistryMapper.selectById(registryId);
        return getServerById(admDockerRegistry.getServerId());
    }

    @Override
    public Server getServerByDockerId(Long dockerId) {
        AdmDocker admDocker = admDockerMapper.selectById(dockerId);
        Long serverId = admDocker.getServerId();
        Server server = getServerById(serverId);
        return server;
    }

    /**
     * 查询虚拟机服务器
     *
     * @return
     */
    @Override
    public List<ServerVo> getList() {
        QueryWrapper<Server> wrapper = new QueryWrapper<>();
        wrapper.lambda().orderByAsc(Server::getSort).orderByAsc(Server::getIp);
        List<Server> list = this.list(wrapper);
        List<ServerVo> serverVos = BeanUtils.copyList(list, ServerVo.class);
        for (ServerVo vo : serverVos) {
            vo.setIp(vo.getIp() == null ? "" : vo.getIp());
            vo.setUsername(vo.getUsername() == null ? "" : vo.getUsername());
            vo.setMemoryTotal(vo.getMemoryTotal() == null ? "" : vo.getMemoryTotal());
            vo.setMemoryFree(vo.getMemoryFree() == null ? "" : vo.getMemoryFree());
            vo.setCpu(vo.getCpu() == null ? "" : vo.getCpu());
            vo.setPassword(vo.getPassword() == null ? "" : vo.getPassword());
            vo.setVpn(vo.getVpn() == null ? "" : vo.getVpn());
        }
        return serverVos;
    }

    @Override
    public List<Server> getAll() {
        if (cacheUtils.hasKey("getAllServer")) {
            return cacheUtils.getCacheObject("getAllServer");
        }else {
            List<Server> list = serverMapper.selectList(null);
            cacheUtils.setCacheObject("getAllServer", list,60*60*1000*24);
            return list;
        }
    }

    @Override
    public void updateLinuxInfo() {
        QueryWrapper<Server> wrapper = new QueryWrapper<>();
        wrapper.eq("type", 1);
        List<Server> list = this.list(wrapper);
        for (Server server : list) {
            String exec = null;
            Session session = null;
            RemoteSshUtil remoteSshUtil = (RemoteSshUtil) applicationContext.getBean("remoteSshUtil");
            try {
                session = remoteSshUtil.connect(server);
                exec = remoteSshUtil.exec("top -bn 1 -i -c", session);
            } catch (Exception e) {
            } finally {
                remoteSshUtil.close(session);
            }
            if (StringUtils.isNotEmpty(exec)) {
                String[] split = exec.split("\n");
                List<String> stringList = Arrays.asList(split);
                String cpu = getCpu(stringList);
                String memoryTotal = getMmoryTotal(stringList);
                String memoryFree = getmemoryFree(stringList);
                server.setCpu(cpu);
                server.setMemoryFree(memoryFree);
                server.setMemoryTotal(memoryTotal);
                this.updateById(server);
            }
        }
    }

    @Override
    public String getLoclIp() {
        return  "192.168.29.180:8088";
        //return IpUtil.getLocalIp() + ":8088";
    }

    @Override
    public SftpDownloadVo downloadForTemp(QueryFileByPathDto dto) throws Exception {
        String ip = dto.getIp();
        Server server = getServerByIp(ip);
        String path = dto.getPath();
        String directory = path.substring(0, path.lastIndexOf("/"));
        String name = path.substring(path.lastIndexOf("/") + 1);
        String localPath = StringUtils.format(dssProperties.getSystemPrefix()+AutoConstants.LINUX_TEMP_FILE_PREFIX, ip, directory);
        FileUtils.createFilePath(localPath);
        String saveFile = StringUtils.format(dssProperties.getSystemPrefix()+AutoConstants.LINUX_TEMP_FILE, ip, directory, name);
        File file = new File(saveFile);
        if (!file.exists()) {
            file.createNewFile();
        }
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        remoteSshUtil.download(directory, path, saveFile, server);
        String fileToString = FileUtils.readFileToString(saveFile, CharEncoding.UTF_8);
        return SftpDownloadVo.builder().path(saveFile).content(fileToString).fileName(dto.getIp() + "_" + name).linuxPath(directory).originalFileName(name).ip(ip).build();
    }

    @Override
    public void saveContentForTemp(SftpDownloadVo dto) throws Exception {
        String ip = dto.getIp();
        Server server = getServerByIp(ip);
        File file = new File(dto.getPath());
        file.delete();
        //先保存本地文件
        FileUtils.writeStringFile(dto.getPath(), dto.getContent());
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        Session session = remoteSshUtil.connect(server);
        remoteSshUtil.putFile(session, dto.getPath(), dto.getLinuxPath());
        remoteSshUtil.close(session);
    }

    @Override
    public void download(QueryFileByPathDto dto, HttpServletResponse response) throws InterruptedException, IOException {
        String path = dto.getPath();
        String ip = dto.getIp();
        String[] split = path.split("/");
        String outputFileName =  split[split.length - 1];
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        Server server = getServerByIp(ip);
        long currentTimeMillis = System.currentTimeMillis();
        String localPath = dssProperties.getSystemPrefix()+StringUtils.format(AutoConstants.LINUX_DOWNLOAD_FILE, ip, currentTimeMillis,"temp");
        FileUtils.createFilePath(localPath);
        Session session = null;
        Channel channel = null;
        try {
            session = remoteSshUtil.connect(server);
            channel = session.openChannel("sftp");
            channel.connect();
            ChannelSftp sftp = (ChannelSftp) channel;
            remoteSshUtil.download(sftp, path, localPath);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            remoteSshUtil.close(channel);
            remoteSshUtil.close(session);
        }
        InputStream inputStream = Files.newInputStream(Paths.get(localPath + "/" + outputFileName));
        byte[] buffer = new byte[inputStream.available()];
        inputStream.read(buffer);
        inputStream.close();

        // 设置response的header信息
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename="
                .concat(String.valueOf(URLEncoder.encode(outputFileName, "UTF-8"))));

        // 将字节流写入response的输出流中
        OutputStream outputStream = response.getOutputStream();
        outputStream.write(buffer);
        outputStream.flush();
        outputStream.close();
        FileUtils.deleteAllFile(dssProperties.getSystemPrefix()+StringUtils.format(AutoConstants.LINUX_DOWNLOAD_FILE_PREFIX, ip,currentTimeMillis));
    }
    @Override
    public void downloadList(QueryFileByPathDto dto, HttpServletResponse response) throws InterruptedException, IOException {
        String paths = dto.getPath();
        String ip = dto.getIp();
        String[] split = paths.split(",");
        CountDownLatch mainLatch = new CountDownLatch(split.length);
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        Server server = getServerByIp(ip);
        long currentTimeMillis = System.currentTimeMillis();
        String localPath = StringUtils.format(AutoConstants.LINUX_DOWNLOAD_FILE, ip, currentTimeMillis,"temp");
        FileUtils.createFilePath(localPath);
        Arrays.stream(split).<Runnable>map(path -> () -> {
            Session session = null;
            Channel channel = null;
            try {
                session = remoteSshUtil.connect(server);
                channel = session.openChannel("sftp");
                channel.connect();
                ChannelSftp sftp = (ChannelSftp) channel;
                remoteSshUtil.download(sftp, path, localPath);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mainLatch.countDown();
                remoteSshUtil.close(channel);
                remoteSshUtil.close(session);
            }
        }).forEach(threadPoolTaskExecutor::execute);
        mainLatch.await();
        String outputFileName = "linux文件下载.zip";
        String tempZipPath = StringUtils.format(AutoConstants.LINUX_DOWNLOAD_FILE, ip,currentTimeMillis, outputFileName);
        FileUtils.zip(localPath,tempZipPath);
        // 读取zip文件为字节流
        InputStream inputStream = new FileInputStream(tempZipPath);
        byte[] buffer = new byte[inputStream.available()];
        inputStream.read(buffer);
        inputStream.close();

        // 设置response的header信息
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment;filename="
                .concat(String.valueOf(URLEncoder.encode(outputFileName, "UTF-8"))));

        // 将字节流写入response的输出流中
        OutputStream outputStream = response.getOutputStream();
        outputStream.write(buffer);
        outputStream.flush();
        outputStream.close();
        FileUtils.deleteAllFile(StringUtils.format(AutoConstants.LINUX_DOWNLOAD_FILE_PREFIX, ip,currentTimeMillis));
    }

    /**
     * @param dto dto
     * @description: 添加服务器文件
     * @author: dongshoushan
     * @date:2022-11-23 22:22:32
     */
    @Override
    public void addServerFile(ServerFileAddDto dto) {
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        Server server = getServerByIp(dto.getIp());
        if (BooleanTypeEnum.YES.getCode().equals(dto.getDir())) {
            remoteSshUtil.createDir(dto.getFilePath() + "/" + dto.getFileName(), server);
        } else {
            remoteSshUtil.createFile(dto.getFilePath(), dto.getFileName(), server);
        }
    }

    /**
     * @description: 删除服务器文件
     * @author: dongshoushan
     * @date:2022-11-23 23:15:37
     */
    @Override
    public void delServerFile(List<ServerFileDelDto> list) {
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        list.forEach(dto -> {
            Server server = getServerByIp(dto.getIp());
            if (BooleanTypeEnum.YES.getCode().equals(dto.getDir())) {
                remoteSshUtil.deleteDir(dto.getFilePath(), server);
            } else {
                String filePath = dto.getFilePath();
                filePath = filePath.substring(0, filePath.lastIndexOf("/"));
                remoteSshUtil.delete(filePath, dto.getFileName(), server);
            }
        });

    }

    /**
     * @param dto dto
     * @description: 重命名名称服务器文件
     * @author: dongshoushan
     * @date:2022-11-24 09:22:40
     */
    @Override
    public void reNameServerFile(ServerFileReNameDto dto) {
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        Server server = getServerByIp(dto.getIp());
        remoteSshUtil.reNameServerFile(server, dto.getOldFileName(), dto.getNewFileName());
    }

    /**
     * @param registryId 仓库id
     * @description: 删除旧镜像
     * @author: dongshoushan
     * @date:2022-11-24 14:04:52
     */
    @Override
    public void deleteOldImages(String registryId) {
        String[] split = registryId.split("_");
        Arrays.stream(split).forEach(id -> {
            Server server = getServerByRegistryId(Long.parseLong(id));
            AdmDockerRegistry admDockerRegistry = admDockerRegistryMapper.selectById(id);
            RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
            String path = StringUtils.format("{}/docker/registry/v2", admDockerRegistry.getStoragePath());
            remoteSshUtil.deleteDir(path, server);
            String msg = StringUtils.format("清理镜像磁盘碎片成功,ip:{} 目录：{}", server.getIp(), path);
            System.out.println(msg);
        });

    }

    /**
     * @param ip   知识产权
     * @param path 路径
     * @param file 文件
     * @description: 上传
     * @author: dongshoushan
     * @date:2022-11-24 20:13:31
     */
    @Override
    public void upload(String ip, String path, MultipartFile file) {
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        Session session = null;
        try {
            Server server = getServerByIp(ip);
            session = remoteSshUtil.connect(server);
            remoteSshUtil.putFile(session, file, path, file.getSize(), 1L, "serverUpload", server.getIp());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            remoteSshUtil.close(session);
        }
    }

    /**
     * @param dto dto
     * @description: 检查路径是正确
     * @author: dongshoushan
     * @date:2022-11-24 22:32:15
     */
    @Override
    public Boolean checkPathIsTrue(CheckPathIsTrueDto dto) {
        RemoteSshUtil remoteSshUtil = RemoteSshUtil.getRemoteSshUtil();
        Session session = null;
        try {
            Server server = getServerByIp(dto.getIp());
            session = remoteSshUtil.connect(server);
            Channel channel = session.openChannel("sftp");
            channel.connect();
            ChannelSftp sftp = (ChannelSftp) channel;
            sftp.cd(dto.getPath());
            return true;
        } catch (Exception e) {
            log.error("路径：P{}：信息：{}", dto.getPath(), e.getMessage());
            return false;
        } finally {
            remoteSshUtil.close(session);
        }
    }


    private FolderVo getChildren(Long id, List<FolderVo> list) {
        FolderVo root = new FolderVo();
        //查询根
        for (FolderVo vo : list) {
            if (vo.getId().equals(id)) {
                root = vo;
            }
        }
        //查询子集
        List<FolderVo> childList = new ArrayList<>();
        for (FolderVo node : list) {
            if (node.getParentId().equals(root.getId())) {
                FolderVo folderVo = FolderVo.builder().id(node.getId()).title(node.getTitle()).build();
                childList.add(folderVo);
            }
        }
        //子集查询子集
        if (CollectionUtil.isNotEmpty(childList)) {
            for (FolderVo child : childList) {
                FolderVo childRoot = getChildren(child.getId(), list);
                if (root.getChildren() == null) {
                    root.setChildren(new ArrayList<>());
                }
                root.getChildren().add(childRoot);
            }
        }
        return root;
    }

    /**
     * 更新剩余内存
     *
     * @param list
     * @return
     */
    private String getmemoryFree(List<String> list) {
        String s = list.get(3);
        String[] split = s.split(":");
        String s1 = split[1];
        String[] split1 = s1.split(",");

        String all = split1[0];
        String total = all.replace("total", "").trim();

        String unUsed = split1[2];
        String used = unUsed.replace("used", "").trim();
        Long free = Long.parseLong(total) - Long.parseLong(used);
        return StringUtils.computerUnitConversion(free * 1024);
    }

    /**
     * 更新总内存
     *
     * @param list
     * @return
     */
    private String getMmoryTotal(List<String> list) {
        String s = list.get(3);
        String[] split = s.split(":");
        String s1 = split[1];
        String[] split1 = s1.split(",");
        String all = split1[0];
        String total = all.replace("total", "").trim();
        return StringUtils.computerUnitConversion(Long.parseLong(total) * 1024);
    }

    /**
     * 更新cpu使用率
     *
     * @param list
     * @return
     */
    private String getCpu(List<String> list) {
        String s = list.get(2);
        String[] split = s.split(":");
        String s1 = split[1];
        String[] split1 = s1.split(",");
        String unUsed = split1[3];
        String id = unUsed.replace("id", "").trim();
        BigDecimal d1 = new BigDecimal("100");
        BigDecimal d2 = new BigDecimal(id);
        double value = d1.subtract(d2).doubleValue();
        return value + "%";
    }

    public RemoteSshUtil getRemoteSshUtils() {
        Object remoteSshUtil = SpringUtils.getBean("remoteSshUtil");
        return (RemoteSshUtil) remoteSshUtil;
    }
}
